﻿using System.Linq;
using System.Threading.Tasks;
using NEH_SA.Model;
using System.Collections.Generic;
using System;


namespace NEH_SA.NEH
{
    public class NehStandard
    {
        private readonly List<Problem> problemy;
        private int[,] _p;
        private int[,] _r;
        private int[,] _q;
        private int[] _d;
        private int[] _pi;
        private int[] _suma;
        private List<int> permutacja;

        public NehStandard(List<Problem> problemy)
        {
            this.problemy = problemy;
        }

        private void PermutacjaStartowa(Problem problem)
        {


            // int[] _pi = new int[problem.LiczbaZadań + 1];
            //   int[] _suma = new int[problem.LiczbaZadań + 1];

            NumerCzas[] numerCzasList = new NumerCzas[problem.LiczbaZadań + 1];
            numerCzasList[0] = new NumerCzas();

            //_Csum = 0;
            for (int i = 1; i <= problem.LiczbaZadań; ++i)
            {
                //int sum = 0;
                numerCzasList[i] = new NumerCzas();
                for (int j = 1; j <= problem.LiczbaMaszyn; ++j)
                {
                    // Plik_We >> tmp;
                    //Plik_We >> _p[j][i];
                    //sum += _p[j][i];
                    numerCzasList[i].Sum += problem.ZadaniaLista[i - 1].CzasyWykonywańLista[j - 1];
                    // sum += problem.ZadaniaLista[i - 1].CzasyWykonywańLista[j - 1];
                    _p[j, i] = problem.ZadaniaLista[i - 1].CzasyWykonywańLista[j - 1];
                }
                //_suma[i] = sum;
                // _Csum += sum;
                //_pi[i] = i;
                numerCzasList[i].Pi = i;
            }



            //sortowanie według sumy
            Array.Sort(numerCzasList, delegate(NumerCzas numerCzas1, NumerCzas numerCzas2)
            {
                return numerCzas2.Sum - numerCzas1.Sum;
            });

            _pi = NumerCzas.ToArrayPi(numerCzasList);
            _suma = NumerCzas.ToArraySum(numerCzasList);

            //return _pi;
        }

        private Rozwiązanie Algorytm(Problem problem)
        {
            DataTransition.AddProblem(problem.Nazwa);
            DataTransition.Log(string.Format("Start: {0}", problem.Nazwa));
            int i, j, k, Cmax, t, l, Ctmp, poz, _Cmax, _Cmax_Akc;
            Cmax = 0;

            //  _suma = new int[problem.LiczbaZadań];//suma czasow zadan

            _p = new int[problem.LiczbaMaszyn + 1, problem.LiczbaZadań + 1];
            _r = new int[problem.LiczbaMaszyn + 1, problem.LiczbaZadań + 1];
            _q = new int[problem.LiczbaMaszyn + 2, problem.LiczbaZadań + 2];
            _d = new int[problem.LiczbaMaszyn + 1];





            //============== FAZA 1 =====================
            this.PermutacjaStartowa(problem);//permutacja zadan
            //Sortuj_Quick(1,_n);
            // Sortuj_Scalnie(1, _n);

            //============== FAZA 2 ===========================
            for (i = 0; i <= problem.LiczbaMaszyn; ++i)//zerowe czasy dla zad 0
                _r[i, 0] = 0;
            for (j = 0; j <= problem.LiczbaZadań; ++j)//zerowe czasy dla maszyny 0
                _r[0, j] = 0;

            _d[0] = 0;//zerowa odleglosc
            poz = 1;//pozycja na ktora wstawiono zadanie

            for (j = 0; j <= problem.LiczbaZadań; ++j)
                _q[problem.LiczbaMaszyn + 1, j] = 0;//zerowe czasy dla maszyny m+1

            //============= KROK 1 ====================================
            for (k = 2; k <= problem.LiczbaZadań; ++k)//dla kazdego zadania z posortowanej
            {                 //tablicy
                for (i = 1; i <= problem.LiczbaMaszyn; ++i)
                    for (j = poz; j <= k; ++j)//wyznaczenie macierzy r
                        _r[i, j] = Math.Max(_r[i, j - 1], _r[i - 1, j]) + _p[i, _pi[j]];

                for (i = 0; i <= problem.LiczbaMaszyn; ++i)//czasy 0 dla k-tego zadania (wstawianego)
                    _q[i, k] = 0;

                for (i = problem.LiczbaMaszyn; i >= 1; --i)
                    for (j = k - 1; j >= 1; --j)//wyznaczenie macierzy q
                        _q[i, j] = Math.Max(_q[i, j + 1], _q[i + 1, j]) + _p[i, _pi[j]];

                Cmax = _r[problem.LiczbaMaszyn, k];//dla zadania na k-tej pozycji (za wszystkimi)
                i = k;
                t = _pi[k];
                for (j = k - 1; j >= 1; --j)
                {
                    for (l = 1; l <= problem.LiczbaMaszyn; ++l)//maks czas zakonczenia na j-tej pozycji
                        _d[l] = Math.Max(_d[l - 1], _r[l, j - 1]) + _p[l, t];
                    Ctmp = _d[1] + _q[1, j];
                    for (l = 2; l <= problem.LiczbaMaszyn; ++l)//Cmax dla zadania na j-tej pozycji
                        Ctmp = Math.Max(Ctmp, _d[l] + _q[l, j]);

                    if (Ctmp <= Cmax)//jesli mniejszy lub rowny od poprzedniego
                    {
                        Cmax = Ctmp;
                        poz = i = j;
                    }
                }
                for (j = k; j > i; --j)//wstawienie zadania na wlasciwa pozycje
                    _pi[j] = _pi[j - 1];
                _pi[i] = t;
            }
            _Cmax_Akc = _Cmax = Cmax;//zapamietanie Cmax dla rozwiazanego problemu


            //=====================================================

            permutacja = new List<int>(_pi);
            permutacja = permutacja.Skip(1).Select(i1 => i1 - 1).ToList();

            var rozwiązanie = new Rozwiązanie(permutacja, _Cmax, problem.Nazwa, NEH_SA.Model.Algorytm.Neh);
            DataTransition.RemoveProblem(problem.Nazwa);
            DataTransition.Log(string.Format("End: {0}", problem.Nazwa));
            DataTransition.PostępChange();
            return rozwiązanie;
        }

        public IEnumerable<Rozwiązanie> WygenerujeRozwiązania()
        {
            return problemy.Select(Algorytm);
        }
    }
}
